package com.example.fileupload.storage.impl;

import com.example.fileupload.storage.AbstractStorageStrategy;
import com.example.fileupload.storage.StorageStrategy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

/**
 * 本地存储策略实现 - 函数式编程风格
 */
@Slf4j
@Component
public class LocalStorageStrategy extends AbstractStorageStrategy {
    
    @Value("${storage.local.base-path:${user.home}/uploads}")
    private String basePath;
    
    @Override
    public StorageType getStorageType() {
        return StorageType.LOCAL;
    }
    
    @Override
    protected StorageResult doStoreChunk(String chunkPath, InputStream inputStream, long size) {
        try {
            Path fullPath = resolveBasePath().resolve(chunkPath);
            
            // 确保目录存在
            Optional<Path> parentDir = Optional.ofNullable(fullPath.getParent());
            parentDir.filter(dir -> !Files.exists(dir))
                    .ifPresent(dir -> {
                        try {
                            Files.createDirectories(dir);
                            log.debug("创建目录: {}", dir);
                        } catch (Exception e) {
                            throw new RuntimeException("创建目录失败", e);
                        }
                    });
            
            // 写入文件
            Files.copy(inputStream, fullPath);
            log.debug("写入文件: {}, 大小: {}", fullPath, size);
            
            return StorageResult.success(
                    chunkPath,
                    fullPath.toAbsolutePath().toString(),
                    size
            );
            
        } catch (Exception e) {
            log.error("本地存储分片失败: {}", e.getMessage());
            return StorageResult.failure("本地存储分片失败: " + e.getMessage());
        }
    }
    
    @Override
    protected StorageResult doMergeChunks(String finalPath, java.util.List<String> chunkPaths, long totalSize) {
        try {
            Path finalFilePath = resolveBasePath().resolve(finalPath);
            
            // 确保目录存在
            Optional<Path> parentDir = Optional.ofNullable(finalFilePath.getParent());
            parentDir.filter(dir -> !Files.exists(dir))
                    .ifPresent(dir -> {
                        try {
                            Files.createDirectories(dir);
                            log.debug("创建目录: {}", dir);
                        } catch (Exception e) {
                            throw new RuntimeException("创建目录失败", e);
                        }
                    });
            
            // 使用NIO合并分片文件
            try (FileChannel outputChannel = FileChannel.open(finalFilePath,
                    StandardOpenOption.CREATE,
                    StandardOpenOption.WRITE,
                    StandardOpenOption.TRUNCATE_EXISTING)) {
                
                // 函数式处理分片合并
                chunkPaths.stream()
                        .map(this::resolveChunkPath)
                        .forEach(chunkPath -> {
                            try (FileChannel inputChannel = FileChannel.open(chunkPath, StandardOpenOption.READ)) {
                                inputChannel.transferTo(0, inputChannel.size(), outputChannel);
                                log.debug("合并分片: {}", chunkPath);
                            } catch (Exception e) {
                                throw new RuntimeException("合并分片失败: " + chunkPath, e);
                            }
                        });
                
                log.info("文件合并完成: {}, 总大小: {}", finalFilePath, totalSize);
            }
            
            return StorageResult.success(
                    finalPath,
                    finalFilePath.toAbsolutePath().toString(),
                    totalSize
            );
            
        } catch (Exception e) {
            log.error("本地合并文件失败: {}", e.getMessage());
            return StorageResult.failure("本地合并文件失败: " + e.getMessage());
        }
    }
    
    @Override
    protected boolean doDeleteFile(String path) {
        try {
            Path filePath = resolveBasePath().resolve(path);
            
            if (Files.exists(filePath)) {
                if (Files.isDirectory(filePath)) {
                    FileUtils.deleteDirectory(filePath.toFile());
                } else {
                    Files.delete(filePath);
                }
                log.debug("删除文件: {}", filePath);
                return true;
            }
            return false;
            
        } catch (Exception e) {
            log.error("删除文件失败: {}, 错误: {}", path, e.getMessage());
            return false;
        }
    }
    
    @Override
    public boolean exists(String path) {
        try {
            return Files.exists(resolveBasePath().resolve(path));
        } catch (Exception e) {
            log.error("检查文件存在失败: {}", e.getMessage());
            return false;
        }
    }
    
    @Override
    public String getDownloadUrl(String path) {
        try {
            Path filePath = resolveBasePath().resolve(path);
            if (Files.exists(filePath)) {
                return filePath.toAbsolutePath().toString();
            }
            throw new RuntimeException("文件不存在: " + path);
        } catch (Exception e) {
            log.error("获取下载URL失败: {}", e.getMessage());
            throw new RuntimeException("获取下载URL失败: " + e.getMessage());
        }
    }
    
    // ============ 私有辅助方法 - 函数式风格 ============
    
    /**
     * 解析基础路径
     */
    private Path resolveBasePath() {
        return Paths.get(basePath).toAbsolutePath().normalize();
    }
    
    /**
     * 解析分片路径
     */
    private Path resolveChunkPath(String chunkPath) {
        return resolveBasePath().resolve(chunkPath);
    }
    
    /**
     * 从文件路径提取MD5
     */
    private Optional<String> extractMd5FromPath(String path) {
        try {
            String[] parts = path.split("/");
            return parts.length >= 2 ? 
                    Optional.of(parts[parts.length - 2]) : 
                    Optional.empty();
        } catch (Exception e) {
            return Optional.empty();
        }
    }
    
    // ============ 钩子方法重写 ============
    
    @Override
    protected Optional<StorageResult> postMergeChunks(StorageResult result) {
        // 合并完成后，异步清理分片文件
        cleanupChunksAsync(result.getPath());
        return Optional.of(result);
    }
    
    /**
     * 异步清理分片文件
     */
    private void cleanupChunksAsync(String finalPath) {
        CompletableFuture.runAsync(() -> {
            extractMd5FromPath(finalPath)
                    .map(md5 -> resolveBasePath().resolve("chunks").resolve(md5))
                    .ifPresent(chunkDir -> {
                        boolean deleted = doDeleteFile(chunkDir.toString());
                        log.debug("清理分片目录: {}, 结果: {}", chunkDir, deleted);
                    });
        }).exceptionally(throwable -> {
            log.warn("清理分片目录失败: {}", throwable.getMessage());
            return null;
        });
    }
} 